package main

import "fmt"

func main() {
	/*
		1.引发 panic 的情况 ：
		（1）程序主动调用 panic 函数
			panic(xxx) 参数为空接口类型 interface{}，使用可以传递任何类型的参数
		（2）程序产生运行时错误

		2.发生 panic 后，
			程序会从 调用 panic 的函数位置 或 发生 panic 的地方 立即返回，逐层向上执行函数的 defer 语句，
			然后逐层打印函数调用堆栈，直到被 recover 捕获 或 运行到最外层函数而退出

		3.recover：用来捕获 panic，阻止 panic 继续向上传递（拦截运行时错误）
			recover() 与 defer 一起使用
			recover() 只有在 defer 语句后面的函数体内被直接调用才能捕获 panic，终止异常

		4.可以有连续多个 panic 被抛出，但是只能出现在延迟时间（defer）里面，并且只有最后一次的 panic 能被捕获

		5.init 函数中的 panic 只能在 init 函数中捕获，main 函数中无法捕获，因为 init 函数先于 main 函数执行

		6.函数并不能捕获内部新启动 goroutine 所抛出的 panic
			即，如果 在goroutine中抛出了 panic，而该 goroutine 是在函数内部启动的，则该 panic 将无法被外层函数捕获
		原因：panic / recover 机制只能在同一个 goroutine 中起作用，无法跨越多个 goroutine。
			当一个 goroutine 抛出 panic 时，只有直接父 goroutine 才能通过 recover() 函数将其捕获

		7.panic 的使用场景：
		（1）程序遇到了无法正常执行下去的错误，主动调用 panic 函数结束程序运行
		（2）在调试程序时，通过主动调用 panic 实现快速退出，panic 打印出的堆栈能够更快地定位错误

		为了保证程序的健壮性，需要主动在程序的分支流程上使用 recover() 拦截运行时错误
	*/
	//多个 panic 被抛出
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err) //first defer panic
		}
	}()

	//只有最后一次的 panic 能被捕获
	defer func() {
		panic("first defer panic") //最后一次 panic
	}()

	defer func() {
		panic("second panic")
	}()

	panic("main panic")

	fmt.Println("--------------------------------------------------------")

	//recover 的错误使用
	//1.直接跟在 defer 后面不能成功捕获
	defer recover()

	//2.作为 defer 后面的函数的参数，也不能成功不捕获
	defer fmt.Println(recover())

	//3.defer 后面的嵌套函数中也不能成功捕获
	defer func() {
		func() {
			fmt.Println("defer inner1")
			recover()
		}()
	}()

	//recover 的正确使用
	//1..
	defer func() {
		fmt.Println("defer inner2")
		recover()
	}()

	test()
}

func except() {
	if err := recover(); err != nil {
		fmt.Println("except recover: ", err) //except recover:  test panic
	}
}

// 2..
func test() {
	defer except()
	panic("test panic")
}
